package com.medistat.model.utils;

import java.lang.reflect.Method;
import java.util.List;

import org.springframework.util.ReflectionUtils;

import com.medistat.model.StandardPersistentObject;

//TODO: write tests!!!!
public abstract class ModelUtils {
	
	@SuppressWarnings("rawtypes")
	public static <T extends StandardPersistentObject, Z extends StandardPersistentObject> void addDependentToParent(T dependentObject, Z parentObject, Class parentClass, Class classToInvokeParentSetter) throws Exception {
		validateParameters(dependentObject);
		addDependent(dependentObject, parentObject);
		addBackReferenceToParentObject(dependentObject, parentObject, classToInvokeParentSetter);
	}
	
	@SuppressWarnings("rawtypes")
	public static <T extends StandardPersistentObject, Z extends StandardPersistentObject> void addDependentToParent(T dependentObject, Z parentObject, Class classToInvokeParentSetter) throws Exception {
		validateParameters(dependentObject);
		addDependent(dependentObject, parentObject);
		addBackReferenceToParentObject(dependentObject, parentObject, classToInvokeParentSetter);
	}
	
	protected static <T extends StandardPersistentObject> void validateParameters(T dependentObject) throws Exception {
		if (dependentObject == null) {
			throw new Exception("Dependent object should not be null!");
		}
	}
	
	@SuppressWarnings("rawtypes")
	protected static <T extends StandardPersistentObject, Z extends StandardPersistentObject> void addBackReferenceToParentObject(T depenedentObject, Z parentObject, Class classToInvokeSetter) {
		Class[] paramTypes = new Class[1];
		paramTypes[0] = classToInvokeSetter;
		Method methodSetParent = ReflectionUtils.findMethod(depenedentObject.getClass(), "set" + classToInvokeSetter.getSimpleName(), paramTypes);
		Object[] arguments = new Object[1];
		arguments[0] = parentObject;
		ReflectionUtils.invokeMethod(methodSetParent, depenedentObject, arguments);
	}

	protected static <T extends StandardPersistentObject, Z extends StandardPersistentObject> void addDependent(T dependentObject, Z parentObject) {
		String methodName = "getListOf" + dependentObject.getClass().getSimpleName() + "s";
		Method method = ReflectionUtils.findMethod(parentObject.getClass(), methodName);
		@SuppressWarnings("unchecked")
		List<T> result = (List<T>) ReflectionUtils.invokeMethod(method, parentObject);
		result.add(dependentObject);
	}
}
